import type { Stats } from 'fs'
import * as path from 'path'
import {
  importFileExtensions,
  relevantFileExtensions,
} from '@src/lang/wasmUtils'
import * as fs from 'fs/promises'

import { PROJECT_ENTRYPOINT } from '@src/lib/constants'
import { getInVariableCase } from '@src/lib/utils'
import type { ModuleType } from '@src/lib/wasm_lib_wrapper'
import { isExtensionAnImportExtension } from '@src/lib/paths'

/// Get the current project file from the path.
/// This is used for double-clicking on a file in the file explorer,
/// or the command line args, or deep linking.
export default async function getCurrentProjectFile(
  pathString: string,
  wasmInstance?: ModuleType
): Promise<string | Error> {
  // Extract the values into an array
  const allFileImportFormats: string[] = importFileExtensions(wasmInstance)
  const relevantExtensions: string[] = relevantFileExtensions(wasmInstance)
  const shouldWrapExtension = (extension: string) => {
    return isExtensionAnImportExtension(extension, allFileImportFormats)
  }

  // Fix for "." path, which is the current directory.
  let sourcePath = pathString === '.' ? process.cwd() : pathString

  // URL decode the path.
  sourcePath = decodeURIComponent(sourcePath)

  // If the path does not start with a slash, it is a relative path.
  // We need to convert it to an absolute path.
  sourcePath = path.isAbsolute(sourcePath)
    ? sourcePath
    : path.join(process.cwd(), sourcePath)

  let stats: Stats
  try {
    stats = await fs.stat(sourcePath)
  } catch (error) {
    return new Error(
      `Unable to access the path: ${sourcePath}. Error: ${error}`
    )
  }

  // If the path is a directory, let's assume it is a project directory.
  if (stats.isDirectory()) {
    // Walk the directory and look for a kcl file.
    const files = await fs.readdir(sourcePath)
    const kclFiles = files.filter((file) => path.extname(file) === '.kcl')

    if (kclFiles.length === 0) {
      let projectFile = path.join(sourcePath, PROJECT_ENTRYPOINT)
      // Check if we have a main.kcl file in the project.
      try {
        await fs.access(projectFile)
      } catch {
        // Create the default file in the project.
        await fs.writeFile(projectFile, '')
      }

      return projectFile
    }

    // If a project entrypoint file exists, use it.
    // Otherwise, use the first kcl file in the project.
    const gotMain = files.filter((file) => file === PROJECT_ENTRYPOINT)
    if (gotMain.length === 0) {
      return path.join(sourcePath, kclFiles[0])
    }
    return path.join(sourcePath, PROJECT_ENTRYPOINT)
  }

  // Check if the extension on what we are trying to open is a relevant file type.
  const extension = path.extname(sourcePath).slice(1).toLowerCase()

  if (!relevantExtensions.includes(extension) && extension !== 'toml') {
    return new Error(
      `File type (${extension}) cannot be opened with this app: '${sourcePath}', try opening one of the following file types: ${relevantExtensions.join(
        ', '
      )}`
    )
  }

  // We were given a file path, not a directory.
  // Let's get the parent directory of the file.
  const parent = path.dirname(sourcePath)

  // If we got an import model file, we need to check if we have a file in the project for
  // this import model.
  // TODO: once we have some sort of a load file into project it would make sense to stop creating these wrapper files
  // and let people save their own kcl file importing
  if (shouldWrapExtension(extension)) {
    const importFileName = path.basename(sourcePath)
    // Check if we have a file in the project for this import model.
    const kclWrapperFilename = `${importFileName}.kcl`
    const kclWrapperFilePath = path.join(parent, kclWrapperFilename)

    try {
      await fs.access(kclWrapperFilePath)
    } catch {
      // Create the file in the project with the default import content.
      const alias = getInVariableCase(importFileName) ?? 'model'
      const content = `// This file was automatically generated by the application when you
// double-clicked on the model file.
// You can edit this file to add your own content.
// But we recommend you keep the import statement as it is.
// For more information on the import statement, see the documentation at:
// https://zoo.dev/docs/kcl-lang/modules
import "${importFileName}" as ${alias}`
      await fs.writeFile(kclWrapperFilePath, content)
    }

    return kclWrapperFilePath
  }

  return sourcePath
}
